package proxyc

import (
	"gitee.com/yysf-xin/dproxy/pkg/app"
	"gitee.com/yysf-xin/dproxy/pkg/db"
	"gitee.com/yysf-xin/dproxy/pkg/model"
	"gitee.com/yysf-xin/dproxy/pkg/rest"
	"gitee.com/yysf-xin/dproxy/pkg/svr"
	"gitee.com/yysf-xin/dproxy/pkg/tools"
	"github.com/go-chi/chi/v5"
	"golang.org/x/net/proxy"
	"net/http"
	"strconv"
	"sync"
)

type TunnelControl struct {
	DB       *db.Client
	Dialer   proxy.Dialer
	services *app.Services
	tMap     map[uint]*svr.TunnelServer
	tMapLock sync.RWMutex
}

func NewTunnelControl(DB *db.Client, dialer proxy.Dialer, services *app.Services) *TunnelControl {
	t := &TunnelControl{DB: DB, Dialer: dialer, services: services, tMap: make(map[uint]*svr.TunnelServer)}

	var tunnels db.TunnelList
	tunnels.ListByEnabled(DB.Db)

	for i := range tunnels {
		err := t.startTunnel(&tunnels[i])
		if err != nil {
			tools.Log.Println("listen addr err", tunnels[i].Local)
		}
	}

	return t
}

func (c *TunnelControl) InitRoute(router chi.Router) {
	router.Get("/", app.Json(nil, c.list))
	router.Route("/{id}/", func(r chi.Router) {
		r.Post("/enable", app.JsonHttp(func() interface{} {
			return &model.Enable{}
		}, c.handlerEnable))
	})
	router.Delete("/{id}", app.JsonHttp(nil, c.Delete))
}
func (c *TunnelControl) handlerEnable(w http.ResponseWriter, r *http.Request, i interface{}) (rs interface{}, err error) {
	var m db.Tunnel
	err = rest.FindModelByReq(r, &m, c.DB.Db)
	if err != nil {
		return
	}
	if i.(*model.Enable).Enabled {
		err = c.startTunnel(&m)
	} else {
		c.stopTunnel(&m)
	}
	return
}

func (c *TunnelControl) Delete(w http.ResponseWriter, r *http.Request, i interface{}) (rs interface{}, err error) {
	var m *db.Tunnel
	err = rest.FindModelByReq(r, &m, c.DB.Db)
	if err != nil {
		return
	}
	c.stopTunnel(m)
	err = c.DB.Db.Delete(m).Error
	return
}

func (c *TunnelControl) startTunnel(t *db.Tunnel) (err error) {
	c.stopTunnel(t)
	s := &svr.TunnelServer{
		Tunnel: t,
		Dialer: c.Dialer,
		Name:   strconv.Itoa(int(t.Id)) + "@Tunnel",
	}
	err = s.Start()
	if err == nil {
		c.tMapLock.Lock()
		c.tMap[t.Id] = s
		c.tMapLock.Unlock()
		c.services.AddService(s)
	}

	return
}

func (c *TunnelControl) stopTunnel(t *db.Tunnel) {
	c.tMapLock.RLock()
	s := c.tMap[t.Id]
	c.tMapLock.RUnlock()

	if s != nil {
		c.services.RemoveService(s)
		s.Stop()
		c.tMapLock.Lock()
		delete(c.tMap, t.Id)
		c.tMapLock.Unlock()
	}
}

func (c *TunnelControl) list(i interface{}) (rs interface{}, err error) {
	var list []*db.Tunnel
	if err = c.DB.Db.Find(&list).Error; err != nil {
		return
	}
	c.tMapLock.RLock()
	for i2 := range list {
		if c.tMap[list[i2].Id] != nil {
			list[i2].Status = 1
		}
	}
	c.tMapLock.RUnlock()

	rs = &list
	return
}
